iT邦幫忙

2021 iThome 鐵人賽

DAY 22
1
Modern Web

從零開始學習 Next.js系列 第 22

Day22 - 錯誤捕捉、全域 CSS、共用 Layout,就用 _app.tsx 來搞定吧!

  • 分享至 

  • xImage
  •  

_app.tsx 可以做什麼?

App 跟 Document 皆是 Next.js 的進入點,而 Document 的層級更高,Document 中的程式碼會先被執行,接著才會執行 App 中的內容。每一個頁面的頂層都包含 App,如果想要讓所有的頁面共享同樣的 Layout、錯誤捕捉、全域的 CSS 等,需要 override 原本 Next.js 的 App,你可以在 /pages 資料夾中建立一個 _app.tsx ,如此一來 Next.js 就會執行客製化的 _app.tsx

整合 NextAuth Provider

在前面的章節「在 Next.js 做 JWT 驗證,使用既有的 Backend API 」有提到如何使用 NextAuth 進行 JWT 驗證,如果想要讓取得使用者驗證的 session 更有效率,可以在 _app.tsx 中新增 NextAuth 的 context provider,讓 useSession 可以在 SSR 的頁面中優先從 context 中取值,這樣就不用每次切換頁面時都會重新打驗證使用者的 API,整合 context provider 至 _app.tsx 有助於效能提升。

import { AppProps } from "next/app";
import { Provider } from "next-auth/client";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <Provider session={pageProps.session}>
      <Component {...pageProps} />
    </Provider>
  );
}

export default MyApp;

Error boundary

Error boundary 是一個從 React 16 版以後出現的概念,它是一個可以在 runtime 時捕捉到客戶端發現的問題,在 React component tree 裡面發生的錯誤會被 componentDidCatch 接住,然後我們可以透過一些方法記錄這些錯誤訊息到伺服器中,讓我們可以儘早地發現問題。

import type { AppProps } from "next/app";

import "../styles/globals.css";
import ErrorBoundary from "./ErrorBoundary";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ErrorBoundary>
      <Component {...pageProps} />
    </ErrorBoundary>
  );
}
export default MyApp;

以下是一個簡易的範例,頁面中有一個按鈕,點擊了之後會顯示 props 物件中的一個屬性,但是實際上 props.someProps.a 不存在,會發生 JavaScript 的錯誤,最後讓 error boundary 捕捉到。

import { NextPage } from "next";
import { useState } from "react";

interface Props {
  someProps: {
    a: string;
  };
}

const Home: NextPage<Props> = (props) => {
  const [visible, setVisible] = useState(false);
  return (
    <>
      {visible && <div>{props.someProps.a}</div>}
      <button onClick={() => setVisible(true)}>click me</button>
    </>
  );
};

export default Home;

但是在 Next.js 中使用 error boundary 的時候要注意,error boundary 能夠捕捉的是 runtime error,但是一個 SSR 的頁面發生的錯誤並沒有辦法讓 componentDIdCatch 抓到錯誤訊息,因為 SSR 頁面會在伺服器端執行完畢,最後把已經渲染完的 HTML 回傳給使用者,如果渲染時發生問題伺服器會直接回傳 HTTP 500,error boundary 沒有辦法 fallback 相對應的 component。

500 畫面

這是一個存在與幾年的 issue「Custom componentDidCatch does not work during SSR #5070」,在這樣的情況下,從 Sentry - next.js 這個解決方案中,看到的不是 error boundary 在 React 中會使用的方式,不過這個會是另一個議題了。

Ant design

Ant design 是世界上第二熱門的 React UI 元件庫,俗話說:「如果我能看得更遠, 那是因為站在巨人的肩膀上」,設計師可以基於目前已經做好的元件進一步擴充,而不是從零開始做起,在已經有非常優秀的元件庫之後,我們在建立產品的前端頁面就會事半功倍。

除了看到 ant design 的好處之外,我們也要了解使用它的風險,緊關它是一個擁有 74K 顆星星的開源專案,但是讓人不得不提到在 2018 發生的聖誕節彩蛋 #13098 搞得許多公司沸沸揚揚,因為 ant design 的目標群眾有許多事 2B 的客戶,在當年突然多出了聖誕節彩蛋讓許多工程師都非常納悶可以這樣開發開源專案嗎?

此外,在 2021 年 2 月時 ant design 的官方 GitHub repo 沒理由地突然被移除,包含官方網站 ant.design 也是突然間消失在網路上,後來索性很快有其他備援,所以沒有讓這件事耽擱太久。但是也許很多人就會開始懷疑這個元件庫的可靠性,前後發生很嚴重的事情,難免會影響大家對它的信心。

以上是題外話,除了發生以上兩件大事之外,我們不得不說它是一個非常優秀的元件庫,現在我們想要在 Next.js 整合 ant design 的話該怎麼辦呢?

Next.js 目前尚未內建支援 less

在目前 Next.js 11.1.2 版本中,Next.js 已經內建支援 .css.scss.sass 的檔案格式,讀者們可以從 next-evn.d.ts 中的 next/types/global 型別定義黨看到 TypeScript 支援以上三種不同格式的檔案。

但是 Next.js 尚未支援 less 的檔案,因此在使用 ant design 時需要修改預設的 theme 就會遇到重重困難,現在可以看到 #23185 即在提供 Next.js 對 less 檔案的支援,可是似乎內建 less 還需要一段時間才會發生。

如果想要在 Next.js 11.1.2 版本中使用 ant design,社群有相對應的解決方案為 next-with-less ,這個插件可以讓我們以最少量的設定整合 ant design 的 theme。

設定 next-with-lessnext-compose-plugins

next-with-less 的使用方式就像是 HOC,需要包在 config 外面,但是我們不可能只需要設定 less 的支援,還需要有其他的設定,所以為了整合其他設定,還需要使用 next-compose-plugins 這個套件。我們使用以下指定安裝兩個套件:

yarn add -D next-with-less next-compose-plugins

接著我們需要修改 next.config.js 中的設定:

// next.config.ts
const withPlugins = require("next-compose-plugins");

const withLess = require("next-with-less");

const plugins = [
  /* ...other plugins... */
  [
    withLess,
    {
      lessLoaderOptions: {
        lessOptions: {
          modifyVars: {
            "primary-color": "#00ccb4",
            "border-radius-base": "4px",
          },
        },
      },
    },
  ],
  /* ...other plugins... */
];

module.exports = withPlugins(plugins, {
  reactStrictMode: true,
});

然後,為了在專案中能夠使用 ant design 的樣式,我們需要在 pages/_app.tsx 中引入 antd.less 這個檔案,這樣才能讓專案全域都可以拿到 ant design 的樣式:

// _app.tsx
import type { AppProps } from "next/app";
import "antd/dist/antd.less";

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}
export default MyApp;

最後,測試看看 ant design 的預設元件 theme 是不是已經被修改,在一個頁面中使用「按鈕元件」,因為 primary-colorborder-radius-base 都被新的設定覆蓋,所以你在畫面上就可以看到修改預設 theme 後的元件。

import { NextPage } from "next";
import { Button } from "antd";

const Home: NextPage = () => {
  return <Button type="primary">my button</Button>;
};

export default Home;

修改預設樣式的 button

Reference


上一篇
Day21 - _ document 可以做什麼呢?
下一篇
Day23 - 在 Next.js 中如何共用 Layout
系列文
從零開始學習 Next.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言